package com.tungse.timerwheel;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

public class MultiLevelTimeWheel {
    // 三级时间轮常量
    private static final int MINUTE_SLOTS = 60;  // 分钟轮60格
    private static final int HOUR_SLOTS = 24;    // 小时轮24格
    private static final int DAY_SLOTS = 7;      // 天轮7格（支持一周）
    private static final long MINUTE_INTERVAL = 60_000; // 1分钟=60,000毫秒

    // 分钟轮（最细粒度）
    private final List<Set<Task>> minuteSlots = new ArrayList<>(MINUTE_SLOTS);
    // 小时轮
    private final List<Set<Task>> hourSlots = new ArrayList<>(HOUR_SLOTS);
    // 天轮（最大粒度）
    private final List<Set<Task>> daySlots = new ArrayList<>(DAY_SLOTS);
    // 线程控制
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private final AtomicBoolean running = new AtomicBoolean(false);
    private final ReentrantLock lock = new ReentrantLock();
    // 当前指针位置
    private int currentMinuteSlot = 0;
    private int currentHourSlot = 0;
    private int currentDaySlot = 0;

    public MultiLevelTimeWheel() {
        // 初始化时间轮槽位
        for (int i = 0; i < MINUTE_SLOTS; i++) minuteSlots.add(new HashSet<>());
        for (int i = 0; i < HOUR_SLOTS; i++) hourSlots.add(new HashSet<>());
        for (int i = 0; i < DAY_SLOTS; i++) daySlots.add(new HashSet<>());
    }

    // 测试用例
    public static void main(String[] args) throws InterruptedException {
        MultiLevelTimeWheel timeWheel = new MultiLevelTimeWheel();
        timeWheel.start();

        // 添加不同延迟时间的任务
        timeWheel.addTask(() -> System.out.println("执行5分钟后的任务"), 5);
        timeWheel.addTask(() -> System.out.println("执行2小时后的任务"), 2 * 60);
        timeWheel.addTask(() -> System.out.println("执行3天后的任务"), 3 * 24 * 60);

        // 运行1分钟后停止
        Thread.sleep(60_000);
        timeWheel.stop();
    }

    public void start() {
        if (running.compareAndSet(false, true)) {
            scheduler.scheduleAtFixedRate(this::advance, MINUTE_INTERVAL, MINUTE_INTERVAL, TimeUnit.MILLISECONDS);
        }
    }

    public void stop() {
        if (running.compareAndSet(true, false)) {
            scheduler.shutdown();
        }
    }

    // 添加任务（延迟时间单位为分钟）
    public void addTask(Runnable task, long delayMinutes) {
        long now = System.currentTimeMillis();
        long deadline = now + delayMinutes * 60_000;
        addTask(new Task(task, deadline));
    }

    private void addTask(Task task) {
        lock.lock();
        try {
            long now = System.currentTimeMillis();
            long delay = task.deadline - now;

            if (delay <= 0) {
                // 立即执行
                task.job.run();
                return;
            }

            // 根据延迟时间选择合适的时间轮
            if (delay <= MINUTE_SLOTS * MINUTE_INTERVAL) {
                // 放入分钟轮
                int slots = (int) ((delay + MINUTE_INTERVAL - 1) / MINUTE_INTERVAL);
                int targetSlot = (currentMinuteSlot + slots) % MINUTE_SLOTS;
                minuteSlots.get(targetSlot).add(task);
            } else if (delay <= 24 * 60 * MINUTE_INTERVAL) {
                // 放入小时轮（延迟<24小时）
                int hours = (int) ((delay + 60 * MINUTE_INTERVAL - 1) / (60 * MINUTE_INTERVAL));
                int targetSlot = (currentHourSlot + hours) % HOUR_SLOTS;
                hourSlots.get(targetSlot).add(task);
            } else {
                // 放入天轮（延迟<7天）
                int days = (int) ((delay + 24 * 60 * MINUTE_INTERVAL - 1) / (24 * 60 * MINUTE_INTERVAL));
                int targetSlot = (currentDaySlot + days) % DAY_SLOTS;
                daySlots.get(targetSlot).add(task);
            }
        } finally {
            lock.unlock();
        }
    }

    // 推进时间轮
    private void advance() {
        lock.lock();
        try {
            // 1. 推进分钟轮
            currentMinuteSlot = (currentMinuteSlot + 1) % MINUTE_SLOTS;
            processCurrentSlot(minuteSlots.get(currentMinuteSlot));

            // 2. 检查小时轮推进
            if (currentMinuteSlot == 0) {
                advanceHourWheel();
            }
        } finally {
            lock.unlock();
        }
    }

    // 推进小时轮
    private void advanceHourWheel() {
        currentHourSlot = (currentHourSlot + 1) % HOUR_SLOTS;
        reallocateTasks(hourSlots.get(currentHourSlot));

        // 检查天轮推进
        if (currentHourSlot == 0) {
            advanceDayWheel();
        }
    }

    // 推进天轮
    private void advanceDayWheel() {
        currentDaySlot = (currentDaySlot + 1) % DAY_SLOTS;
        reallocateTasks(daySlots.get(currentDaySlot));
    }

    // 处理当前槽位的任务
    private void processCurrentSlot(Set<Task> tasks) {
        long now = System.currentTimeMillis();
        Iterator<Task> iterator = tasks.iterator();
        while (iterator.hasNext()) {
            Task task = iterator.next();
            if (task.deadline <= now) {
                // 执行到期任务
                task.job.run();
                iterator.remove();
            }
        }
    }

    // 重新分配高级时间轮的任务
    private void reallocateTasks(Set<Task> tasks) {
        Iterator<Task> iterator = tasks.iterator();
        while (iterator.hasNext()) {
            Task task = iterator.next();
            iterator.remove();
            addTask(task);  // 重新分配到合适的时间轮
        }
    }

    // 任务封装（记录原始执行时间）
    private static class Task {
        final Runnable job;
        final long deadline;  // 精确执行时间（毫秒时间戳）

        Task(Runnable job, long deadline) {
            this.job = job;
            this.deadline = deadline;
        }
    }
}